﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;

namespace Moxiecode.TinyMCE.Settings
{
    public class Setting
    {
        private PropertyInfo _property;
        private object _instance;

        public SettingInfoAttribute MetaInfo { get; private set; }

        public string Name { get { return _property.Name; } }

        public object DefaultValue { get; private set; }

        public object Value { get { return _property.GetValue(_instance, null); } }

        public Setting(PropertyInfo property, object instance)
        {
            if (property == null || instance == null)
                throw new NullReferenceException("A setting cannot have a null property or null instance reference.");
            
            // Validate that this is a setting and get it's info
            Setting.IsSetting(property, true);
            MetaInfo = (SettingInfoAttribute)property.GetCustomAttributes(typeof(SettingInfoAttribute), true)[0];
            
            // Get the default value
            DefaultValueAttribute[] defaultVals = (DefaultValueAttribute[])property.GetCustomAttributes(typeof(DefaultValueAttribute), true);
            DefaultValue = (defaultVals.Length == 1) ? defaultVals[0].Value : null;


            _property = property;
            _instance = instance;
        }

        public string[] Commands
        {
            get 
            {
                // No commands if the value is null or it's configured to never render
                if (Value == null || MetaInfo.Render == RenderProperty.Never) return new string[0];

                // No commands if it is a default value and it is not always renderable
                if (Value.Equals(DefaultValue) && MetaInfo.Render != RenderProperty.Always) return new string[0];
                if (Convert.GetTypeCode(Value) == TypeCode.Object)
                {
                    if (Value is ICollection)
                        return CollectionSettingCommands((ICollection)Value);
                    else
                    {
                        TypeConverter converter = TypeDescriptor.GetConverter(Value);
                        if (converter.CanConvertTo(typeof(string)))
                            return new string[] { Format((string)converter.ConvertTo(Value, typeof(string))) };
                        return null;
                    }
                }
                else return new string[] { Format(Value.ToString()) };
            }
        }

        private string[] CollectionSettingCommands(ICollection collection)
        {
            if (collection.Count < 1) return new string[0];
            int i = 1;
            List<string> cmds = new List<string>();
            foreach (object o in collection)
            {
                TypeConverter tc = TypeDescriptor.GetConverter(o);
                if (tc.CanConvertTo(typeof(string)))
                {
                    string item = (string)tc.ConvertTo(o, typeof(string));
                    if (MetaInfo.MultiCommandCollection) item = Format(MetaInfo.Name + i, item);
                    if (!String.IsNullOrEmpty(item)) cmds.Add(item);
                }
                i++;
            }

            if (MetaInfo.MultiCommandCollection) return cmds.ToArray();
            else
            {
                return new string[] { Format(String.Join(",", cmds.ToArray())) };
            }
        }

        private string Format(string value) { return Format(MetaInfo.Name, value); }
        private string Format(string name, string value)
        {
            string finalCmd;
            if (MetaInfo.IsUri)
            {
                if (value[0] == '~') value = VirtualPathUtility.ToAbsolute(value);
                else if (!Uri.IsWellFormedUriString(value, UriKind.RelativeOrAbsolute))
                    throw new ArgumentException("Setting \"" + Name + "\" has been configured as a url setting but does not contain a valid uri.");
            }
            if (String.IsNullOrEmpty(name)) name = _property.Name.ToLower();
            try { finalCmd = String.Format(new SettingFormatter(), MetaInfo.Format, name, value); }
            catch
            { 
                try { finalCmd = String.Format(new SettingFormatter(), MetaInfo.Format, name); }
                catch { throw; }
            }
            return finalCmd;
        }

        #region Static Methods

        public static bool IsSetting(PropertyInfo property)
        {
            return Setting.IsSetting(property, false);
        }

        public static bool IsSetting(PropertyInfo property, bool throwIfFalse)
        {
            if(property.GetCustomAttributes(typeof(SettingInfoAttribute), true).Length == 1) return true;
            if (throwIfFalse) throw new ArgumentException("Property has not been configured as a setting.", "property");
            else return false;
        }

        #endregion
    }
}
